import { h, ref, isRef, unref, markRaw } from 'vue';
import { get } from 'lodash-es';
import { Modal, message } from 'ant-design-vue';
import { getUUID } from '@gis/lib/utils/index.js';
import { viewImages } from '@gis/lib/components/img-viewer/viewer.js';
import { uploadFiles } from '@gis/lib/api/index.js';

export const acceptTypes = {
  image: {
    type: 1,
    title: '图片',
    mime: 'image/gif,image/png,image/jpeg,image/bmp,image/webp',
    suffix: 'gif,png,jpg,jpeg,bmp,webp',
  },
  audio: {
    type: 2,
    title: '音频',
    mime: 'audio/midi,audio/mpeg,audio/webm,audio/ogg,audio/wav,audio/x-m4a,audio/x-mpegurl,audio/aac,audio/flac',
    suffix: 'midi,mp3,ogg,wav,m3u,m4a,aac,flac',
  },
  video: {
    type: 3,
    title: '视频',
    mime: 'video/mp4,video/mpeg,video/webm,video/ogg,video/x-flv,video/x-msvideo,video/quicktime,video/x-ms-wmv,video/x-matroska',
    suffix: 'mp4,mpg,mpe,mpeg,webm,ogg,flv,avi,mov,wmv,mkv',
  },
  doc: {
    type: 4,
    title: '文档',
    mime: 'text/plain,text/csv,application/pdf,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document,application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.ms-excel.sheet.macroenabled.12,application/vnd.ms-excel.sheet.binary.macroenabled.12,application/vnd.ms-powerpoint,application/vnd.openxmlformats-officedocument.presentationml.presentation,application/vnd.ms-powerpoint.presentation.macroenabled.12',
    suffix: 'txt,csv,pdf,doc,dot,docx,dotx,xls,xlt,xla,xlsx,xlsm,xlsb,ppt,pptx,pptm',
  },
};

export class WrapFile {
  constructor(file, getter) {
    this.uid = getUUID();
    this.name = ref('');
    this.type = ref('');
    this.suffix = ref('');
    this.url = ref('');
    this.thumbUrl = ref('');
    this.originFileObj = null;
    this.status = ref('waiting');
    if (file instanceof File) {
      this.name.value = file.name;
      this.type.value = getFileType({ name: this.name.value }, 'type');
      this.originFileObj = markRaw(file);
      this.suffix.value = getSuffix(this.name.value);
      getBase64(file).then((url) => {
        this.thumbUrl.value = url;
      });
    } else if (file && (typeof file === 'string' || typeof file === 'object')) {
      let url, name;
      if (typeof getter === 'string') {
        name = getter;
      } else if (typeof getter === 'function') {
        let res = getter(file);
        if (typeof res === 'string') {
          name = res;
        } else if (res && typeof res === 'object') {
          ({ url, name } = res);
        }
      }
      if (typeof file === 'string') {
        url = file;
      }
      this.name.value = name || getUrlName(url);
      this.type.value = getFileType({ name: this.name.value }, 'type');
      this.suffix.value = getSuffix(this.name.value);
      this.thumbUrl.value = url;
      this.url.value = url;
      this.status.value = 'done';
    }
  }
  set(key, value) {
    isRef(this[key]) ? (this[key].value = value) : (this[key] = value);
  }
  get(key) {
    return unref(this[key]);
  }
}

export function getFileType(file, valueKey) {
  if (!file || typeof file !== 'object') {
    return;
  }
  let includes = (types, type) => {
    return types.split(',').includes(type);
  };
  for (let key in acceptTypes) {
    let opt = acceptTypes[key];
    let { mime, suffix } = opt;
    if (includes(mime, unref(file.type)) || includes(suffix, getSuffix(unref(file.name)))) {
      return typeof valueKey === 'string' && valueKey ? opt[valueKey] : key;
    }
  }
}

export function getBase64(file) {
  return new Promise((resolve, reject) => {
    let reader = new FileReader();
    reader.onerror = (error) => reject(error);
    reader.onload = () => resolve(reader.result);
    reader.readAsDataURL(file);
  });
}

export function getSuffix(str) {
  if (typeof str === 'string' && str.indexOf('.') > 0) {
    return str.split('.').pop().toLocaleLowerCase();
  }
  return '';
}

export function getUrlName(str) {
  return typeof str === 'string' ? str.split('/').pop() : '';
}

export function getAcceptTypes(accept, options) {
  if (!accept || accept === '*' || typeof accept !== 'string') {
    return { mime: '*', suffix: '' };
  }
  accept = accept.split(',');
  options = Object.assign({}, options);
  let { useDefault, enableNone } = options;
  let fullbackVal = useDefault !== false ? acceptTypes.image : enableNone === true ? null : { mime: '*', suffix: '' };
  let res = { mime: '', suffix: '' };
  res = accept.reduce((res, item) => {
    let opt = acceptTypes[accept] || fullbackVal;
    if (!opt) return res;
    return {
      mime: [res.mime, get(opt, 'mime')].join(','),
      suffix: [res.suffix, get(opt, 'suffix')].join(','),
    };
  }, res);
  res.mime.replace(/^,/, '');
  res.suffix.replace(/^,/, '');
  res = res.mime ? res : fullbackVal;
  return res;
}

export function getWrapFile(file, getter) {
  if (file instanceof WrapFile) return file;
  return new WrapFile(file, getter);
}

export function getWrapFileList(values, iteratee) {
  /* 主要用于处理接口返回的文件地址用于回显 */
  if (values && typeof values === 'string') {
    values = values.split(',');
  }
  if (!Array.isArray(values)) {
    return [];
  }
  if (typeof iteratee !== 'function') {
    iteratee = () => {};
  }
  return values.reduce((res, item, index) => {
    if (!item) return res;
    if (typeof item === 'string') {
      let name = iteratee(item, index);
      res.push(getWrapFile(item, name));
    } else if (typeof item === 'object') {
      let data = iteratee(item, index);
      if (data && data.url) {
        res.push(getWrapFile(data.url, data.name));
      }
    }
    return res;
  }, []);
}

export function isImage(file) {
  return getFileType(file) === 'image';
}

export function isVideo(file) {
  return getFileType(file) === 'video';
}

export function isAudio(file) {
  return getFileType(file) === 'audio';
}

export function isDoc(file) {
  return getFileType(file) === 'doc';
}

export function isFileExist(file, fileList, notice) {
  if (!file || !fileList || !fileList.length) {
    return false;
  }
  file = getWrapFile(file);
  let exist = fileList.some((item) => {
    let sameName = get(item, 'name', '') === file.get('name');
    let sameType = get(item, 'type', '') === file.get('type');
    return sameName && sameType;
  });
  if (exist && notice !== false) {
    Modal.info({
      title: '文件已存在！',
      content: '请重新选择文件或者重命名文件后再次尝试~',
    });
  }
  return exist;
}

export function isFileAcceptable(file, accept, notice) {
  if (!accept || accept === '*' || typeof accept !== 'string') {
    return true;
  }
  let opt = getAcceptTypes(accept, { useDefault: false, enableNone: true });
  if (!opt) {
    message.error('文件类型不正确');
    return false;
  }
  let isMatch = (str, val) => {
    return str.split(',').includes(val);
  };
  let type = get(getWrapFile(file), 'originFileObj.type');
  let matched = isMatch(opt.mime, type);
  if (!matched && notice !== false) {
    handleNotice(accept);
  }
  return matched;
}

export function handleNotice(accept) {
  let list = [];
  if (Array.isArray(accept)) {
    list = accept;
  } else if (typeof accept === 'string') {
    list = accept.split(',');
  }
  list = list.reduce((res, item) => {
    let opt = acceptTypes[item];
    if (opt) res.push(opt);
    return res;
  }, []);
  Modal.info({
    title: h('div', { style: 'color: red' }, '文件格式不符合要求！'),
    content: h('div', {}, [
      h('div', { style: 'margin: 0 0 16px' }, '仅支持如下格式：'),
      list.map(({ title, suffix }) => {
        suffix = suffix.replace(/,/g, '，');
        return h('div', { style: 'display: flex;' }, [
          h('div', { style: 'flex: none' }, title + '：'),
          h('div', { style: 'white-space: pre-wrap;word-break: break-all;letter-spacing: 0' }, suffix),
        ]);
      }),
    ]),
  });
}

export async function handlePreview(file) {
  let url = null;
  if (file instanceof File) {
    url = await getBase64(file);
  } else if (file && typeof file === 'object') {
    url = unref(file.url);
    if (!url && file.originFileObj instanceof File) {
      url = await getBase64(file.originFileObj);
    }
  }
  if (url) viewImages(url);
}

export async function handleUpload(file, options) {
  options = Object.assign({ filePath: '/files' }, options);
  // debugger;
  file = getWrapFile(file);
  if (!!file.get('url')) return file;
  let formData = new FormData();
  formData.append('file', file.originFileObj);
  formData.append('filePath', options.filePath);
  let res = await uploadFiles(formData).catch(() => {
    file.set('url', '');
    file.set('status', 'fail');
  });
  if (res && res.code === 200) {
    let url = get(res, 'data.url');
    file.set('url', url);
    file.set('status', 'done');
  } else {
    file.set('url', '');
    file.set('status', 'fail');
    message.error('文件“ ' + file.get('name') + ' “上传失败!');
    return Promise.reject(false);
  }
  return file;
}
